#include "stdafx.h"

#include "CommandLineArgsParser.h"

#include <stdlib.h>
#include <string.h>
#include <fstream>

const char* USAGE =
"\nOPTIONS:\n"
"-help              - prints this help\n"
"-input <file>      - input file (required)\n"
"-output <file>     - output file (optional)\n"
"-inputt <file>     - input transform matrix file (optional)\n"
"-outputt <file>    - output transform matrix file (optional)\n"
"-dim <value>       - specify number of dimensions after reduction (optional)\n";

const int ERROR_MISSING_INPUT_ARGUMENT = 0;
const int ERROR_MISSING_ARGUMENT_VALUE = 1;
const int ERROR_UNSPECIFIED_OUTPUT     = 2;
const int ERROR_INVALID_DIM_VALUE      = 3;
const int ERROR_INVALID_INPUT_FILE     = 4;
const int ERROR_INVALID_OUTPUT_FILE    = 5;
const int ERROR_INVALID_INPUTT_FILE    = 6;
const int ERROR_INVALID_OUTPUTT_FILE   = 7;
const int ERROR_INCOMPLETE_REDUCTION_ARGS = 8;

CommandLineArgsParser::CommandLineArgsParser(int argc, char ** argv) {
    errorCode = dimensions= -1;
    inputPath = outputPath = inputTranformPath = outputTranformPath = NULL;
    help = getArgumentIndex(argc, argv, "-help") > 0;
    if (help) {
        return;
    }

    int inputIndex = getArgumentIndex(argc, argv, "-input") + 1;
    int outputIndex = getArgumentIndex(argc, argv, "-output") + 1;
    int inputtIndex = getArgumentIndex(argc, argv, "-inputt") + 1;
    int outputtIndex = getArgumentIndex(argc, argv, "-outputt") + 1;
    int dimIndex = getArgumentIndex(argc, argv, "-dim") + 1;

    if (inputIndex <= 0) {
        errorCode = ERROR_MISSING_INPUT_ARGUMENT;
        return;
    }

    bool missingArgumentValue = inputIndex >= argc
            || outputIndex >= argc || inputtIndex >= argc
            || outputtIndex >= argc || dimIndex >= argc;
    if (missingArgumentValue) {
        errorCode = ERROR_MISSING_ARGUMENT_VALUE;
        return;
    }

    if (outputIndex <= 0 && outputtIndex <= 0) {
        errorCode = ERROR_UNSPECIFIED_OUTPUT;
        return;
    }

    if (dimIndex > 0) {
        dimensions = atoi(argv[dimIndex]);
        if (dimensions <= 0) {
            return;
        }
    }

    if (!checkFileExists(argv[inputIndex])) {
        errorCode = ERROR_INVALID_INPUT_FILE;
        return;
    }
    inputPath = argv[inputIndex];

    if (outputIndex > 0) {
        outputPath = argv[outputIndex];
    }

    if (inputtIndex > 0) {
        if (!checkFileExists(argv[inputtIndex])) {
            errorCode = ERROR_INVALID_INPUTT_FILE;
            return;
        }
        inputTranformPath = argv[inputtIndex];
    }

    if (outputtIndex > 0) {
        outputTranformPath = argv[outputtIndex];
    }

    if ((dimensions > 0 && outputIndex <= 0) || (dimensions <= 0 && outputIndex > 0)) {
        errorCode = ERROR_INCOMPLETE_REDUCTION_ARGS;
        return;
    }

}

const char * CommandLineArgsParser::getErrorMessage() {
    switch (errorCode) {
    case -1:
        return "No error";
    case ERROR_MISSING_INPUT_ARGUMENT:
        return "Unspecified input file";
    case ERROR_MISSING_ARGUMENT_VALUE:
        return "Missing value for specified argument";
    case ERROR_INVALID_DIM_VALUE:
        return "Invalid dimension value (positive integer only)";
    case ERROR_UNSPECIFIED_OUTPUT:
        return "Unspecified output file (use either output or outputt)";
    case ERROR_INVALID_INPUT_FILE:
        return "Invalid input file";
    case ERROR_INVALID_OUTPUT_FILE:
        return "Invalid output file";
    case ERROR_INVALID_INPUTT_FILE:
        return "Invalid transform matrix input file";
    case ERROR_INVALID_OUTPUTT_FILE:
        return "Invalid transform matrix output file";
    case ERROR_INCOMPLETE_REDUCTION_ARGS:
        return "To perform PCA dimension reduction you must specify both dim and output arguments";
    }

    return "Undefined args error";
}

const char * CommandLineArgsParser::getUsageMessage() {
    return USAGE;
}

int CommandLineArgsParser::getArgumentIndex(int argc, char **argv, const char * argument) {
    for(int i = 0; i < argc; ++i) {
        if(strcmp(argument, argv[i]) == 0) {
            return i;
        }
    }
    return -1;
}

bool CommandLineArgsParser::checkFileExists(const char * fileName) {
    std::ifstream file(fileName);
    return file.good();
}
